package com.cherrypt.api.core.service.impl;

import com.cherrypt.api.common.constant.WebConst;
import com.cherrypt.api.core.service.PeerService;
import com.cherrypt.api.model.dataobject.Peer;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

@Service
public class PeerServiceImpl implements PeerService {
    private static final Logger logger = LoggerFactory.getLogger(PeerServiceImpl.class);

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private ValueOperations<String, Object> valueOperations;

    @Value("${api.config.redis.peer.prefix}")
    private String peerPrefix;

    @Value("${api.config.redis.peer.completePrefix}")
    private String completeCountPrefix;

    @Value("${api.config.redis.peer.downloadPrefix}")
    private String downloadPrefix;

    @Value("${api.config.redis.peer.exp}")
    private long peerExp;

    @Override
    public void savePeer(int tid, Peer peer) {
        savePeerByPrefix(tid, peer, peerPrefix);
    }

    private void savePeerByPrefix(int tid, Peer peer, String prefix) {
        valueOperations.set(prefix + tid +
                        WebConst.REDIS_REGEX + peer.getIp() +
                        WebConst.REDIS_REGEX + peer.getPort() +
                        WebConst.REDIS_REGEX + peer.getPeerId(),
                peer, Duration.ofSeconds(peerExp));
    }

    @Override
    public void saveAllPeer(int tid, List<Peer> peerList) {
        peerList.forEach(peer -> {
            savePeer(tid, peer);
        });
    }

    @Override
    public Peer getPeer(int tid, Peer peer) {
        return getPeerByPrefix(tid, peer, peerPrefix);
    }

    private Peer getPeerByPrefix(int tid, Peer peer, String prefix) {
        return (Peer) valueOperations.get(prefix + tid +
                WebConst.REDIS_REGEX + peer.getIp() +
                WebConst.REDIS_REGEX + peer.getPort() +
                WebConst.REDIS_REGEX + peer.getPeerId());
    }

    @Override
    public List<Peer> getPeerList(int tid) {
        return getPeerListByPrefix(tid, peerPrefix);
    }

    private List<Peer> getPeerListByPrefix(int tid, String prefix) {
        List<Peer> peerList = new ArrayList<>();
        Set<String> keys = redisTemplate.keys(prefix + tid + WebConst.REDIS_ALL_KEY);
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                peerList.add((Peer) valueOperations.get(key));
            }
        }
        return peerList;
    }

    @Override
    public List<Peer> getAllPeer(int tid) {
        List<Peer> list = new ArrayList<>();
        // 聚合peerList和downloadPeerList
        List<Peer> peerList = getPeerList(tid);
        List<Peer> downloadPeerList = getDownloadPeerList(tid);
        list.addAll(peerList);
        list.addAll(downloadPeerList);
        return list;
    }

    @Override
    public int getPeerLen(int tid) {
        return getPeerLenByPrefix(tid, peerPrefix);
    }

    @Override
    public void updatePeerTTL(int tid, Peer peer) {
        updatePeerTTLByPrefix(tid, peer, peerPrefix, peerExp);
    }

    private void updatePeerTTLByPrefix(int tid, Peer peer, String prefix, long timeout) {
        redisTemplate.expire(prefix + tid +
                WebConst.REDIS_REGEX + peer.getIp() +
                WebConst.REDIS_REGEX + peer.getPort() +
                WebConst.REDIS_REGEX + peer.getPeerId(), timeout, TimeUnit.SECONDS);
    }

    private int getPeerLenByPrefix(int tid, String prefix) {
        Set<String> keys = redisTemplate.keys(prefix + tid + WebConst.REDIS_ALL_KEY);
        if (keys != null && keys.size() > 0) {
            return keys.size();
        }
        return 0;
    }

    @Override
    public void removePeer(int tid, Peer peer) {
        removePeerByPrefix(tid, peer, peerPrefix);
    }

    private void removePeerByPrefix(int tid, Peer peer, String prefix) {
        redisTemplate.delete(prefix + tid +
                WebConst.REDIS_REGEX + peer.getIp() +
                WebConst.REDIS_REGEX + peer.getPort() +
                WebConst.REDIS_REGEX + peer.getPeerId());
    }

    @Override
    public boolean existPeer(int tid, Peer peer) {
        return existPeerByPrefix(tid, peer, peerPrefix);
    }

    private boolean existPeerByPrefix(int tid, Peer peer, String prefix) {
        if (StringUtils.isNotBlank(peer.getIp()) && peer.getIp() != null && StringUtils.isNotBlank(peer.getPeerId())) {
            return redisTemplate.hasKey(prefix + tid +
                    WebConst.REDIS_REGEX + peer.getIp() +
                    WebConst.REDIS_REGEX + peer.getPort() +
                    WebConst.REDIS_REGEX + peer.getPeerId());
        } else {
            return false;
        }
    }

    @Override
    public void saveDownloadPeer(int tid, Peer peer) {
        savePeerByPrefix(tid, peer, downloadPrefix);
    }

    @Override
    public Peer getDownloadPeer(int tid, Peer peer) {
        return getPeerByPrefix(tid, peer, downloadPrefix);
    }

    @Override
    public List<Peer> getDownloadPeerList(int tid) {
        return getPeerListByPrefix(tid, downloadPrefix);
    }

    @Override
    public int getDownloadPeerLen(int tid) {
        return getPeerLenByPrefix(tid, downloadPrefix);
    }

    @Override
    public void removeDownloadPeer(int tid, Peer peer) {
        removePeerByPrefix(tid, peer, downloadPrefix);
    }

    @Override
    public boolean existDownloadPeer(int tid, Peer peer) {
        return existPeerByPrefix(tid, peer, downloadPrefix);
    }

    @Override
    public int getComplete(int tid) {
        Object o = valueOperations.get(completeCountPrefix + tid);
        if (o != null) {
            return (int) o;
        }
        return 0;
    }

    @Override
    public void completeIncrOne(int tid) {
        valueOperations.increment(completeCountPrefix + tid);
    }

    @Override
    public void completeIncr(int tid, int incr) {
        valueOperations.increment(completeCountPrefix + tid, incr);
    }

    @Override
    public void updateDownloadPeerTTL(int tid, Peer peer) {
        updatePeerTTLByPrefix(tid, peer, downloadPrefix, peerExp);
    }
}
